package GA;

import java.util.*;

import Operations.CreateObject;
import Operations.Invoke;
import Operations.Operation;

import util.Filter;
@SuppressWarnings("unchecked")
public class Chromosome implements Comparable, Cloneable{
	/** the test cluster */
	private final Map<String, Class<?>> cluster;
	/** the reference factory */
	private final Map<String, String[]>  refFactory;
	/** the constructors list of class under test    */
	private  Map<String, List<CreateObject>> constructors;
	
	/** the test as sequence of operation */
	private  List<Operation> test;
	private  float fitness = 0;
	private  float branchDistance=0;

	public Chromosome(Map<String, Class<?>> cluster, Map<String, String[]>  refFactory,Map<String, List<CreateObject>> constructors, List<Operation> test) {
		this.cluster = cluster;
		this.refFactory = refFactory;
		this.test = test;
		this.constructors=constructors;
	}
	public int getSize() {
		return this.test.size();
	}

	public Map<String, List<CreateObject>>  getConstructors() {
		return constructors;
	}

	public Map<String, Class<?>> getCluster() {
		return cluster;
	}
	public  Map<String, String[]>  getRefFactory() {
		return refFactory;
	}

	public List<Operation> getTest() {
		return test;
	}
	public void setTest(List<Operation>  test) {
		this.test=test;
	}
	public void setConstructors(Map<String, List<CreateObject>> constructors) {
		this.constructors=constructors;
	}

	public Map<String, String[]> getReferenceFactory() {
		return refFactory;
	}
	
	public void setFitness(float fitness) {
		this.fitness = fitness;
	}
	public float getFitness() {
		return this.fitness;
	}

	public void setBranchDistance(float branchDistance) {
		this.branchDistance = branchDistance;
	}
	public float getBranchDistance() {
		return this.branchDistance;
	}

	@Override
	public Chromosome clone() {
		List<Operation> ops = new LinkedList<Operation>();
		for (int i = 0; i < test.size(); i++)
			ops.add( (Operation) test.get(i).clone());
	
		Map<String, List<CreateObject>> constructorz= new LinkedHashMap <String, List<CreateObject>> ();
		
		List<String> consKeys = new ArrayList<String>(constructors.keySet()); 

		for(int consIndx=0;consIndx<consKeys.size();consIndx++){
			String cName =(String) consKeys.get(consIndx);
			List <CreateObject>  cnsList=constructors.get(cName);
			List <CreateObject> constructorList=new LinkedList<CreateObject>();
		
			for (int i = 0; i < cnsList.size(); i++) {
				CreateObject c=	(CreateObject) cnsList.get(i).clone();
				constructorList.add(c);
			}
			constructorz.put(cName, constructorList);
		}
		return new Chromosome(cluster, refFactory,constructorz, ops);
	}
	public int compareTo(Object o) {
		Chromosome id = (Chromosome) o;
		return java.lang.Float.compare(id.getFitness(), fitness);
	}
	
	List <String> repositoryTpe=new LinkedList<String>();
	StringBuilder cons = new StringBuilder();
	@Override
	public String toString() {
		cons = new StringBuilder();
		repositoryTpe=new LinkedList<String>();
		StringBuilder methods = new StringBuilder();
		StringBuilder sb = new StringBuilder();
		
		for(Operation op : test) {
			if(op==null) continue;
			methods.append(" ").append(op.toString()).append(";");
			if(op instanceof Invoke ){
				addConstrutor((Invoke) op);
			}
		}
		sb.append(	cons.toString());
		sb.append( methods.toString() + " fit: "+ this.getFitness() + "  BrDis= "+ this.branchDistance); 
		return sb.toString();
	}
	public void addConstrutor(Invoke op) {
//		System.out.println(" Chromosome.addConstrutor op: "+ op.toString());
		ConstructorDeclared cut = null;
		 MethodDeclared method = op.getMethod();
		 if (!method.isStatic()) {
			String objRef = op.getThis();
			cut = getConstrutor(objRef);
		 }
		// System.out.println("Chromosome.class--method: "+ op.toString());
		
		 final Class[] paramsTypes = method.getParameterTypes();
		for (int j = 0; j < paramsTypes.length; j++){
			Class<?> clz=paramsTypes[j];
			if(Filter.isObjectType(clz))
				 clz=method.getReplacementObjectParam();

			if(!Filter.isPrimitiveOrStringType(clz) )
				if(op.getParams()[j]!=null)
				if(!repositoryTpe.contains(op.getParams()[j]))
					 cut=getConstrutor(op.getParams()[j]);
		}
	}
	
	public ConstructorDeclared getConstrutor(String objRef ){

		String cName = null;
		ConstructorDeclared cut = null;
		if (repositoryTpe.contains(objRef)) return null;
		
		List<String> keys = new ArrayList<String>(refFactory.keySet());
		for (int i = 0; i < keys.size(); i++) {
			refFactory.get(keys.get(i)).toString();
			String[] references = refFactory.get(keys.get(i));
			Arrays.sort(references);
			if (Arrays.binarySearch(references, objRef) >= 0)
				cName = (String) keys.get(i).toString();
		}
		if (cName == null)
			System.out.println("Error: Reciever is null" + objRef);
		List<CreateObject> listObject=constructors.get(cName);
		if (listObject != null) {
			for(CreateObject obj : listObject){
				cut=obj.getConstructor();
				if (cut.getRefrence().equals(objRef))
					break;
				else
					cut = null;
			}
			repositoryTpe.add(objRef);
			cons.append(cut.toString()+ ";\n");
		}

		return cut;
	}
	
}